#include <time.h>
#include <process.h>
#include "CSGUnpack.h"

CSGUnpack::CSGUnpack(string in_file_path, string out_file_path)
{
	INIT_LOG();
	LOG("input_file(\"%s\")", in_file_path.c_str());
	LOG("output_file(\"%s\")", out_file_path.c_str());

	m_in_fp = NULL;
	m_out_fp = NULL;
	m_memory_data.m_begin = NULL;
	m_slice_arr = NULL;

	InitializeCriticalSection(&m_critical_section);

	if (in_file_path == out_file_path)
	{
		return;
	}
	fopen_s(&m_in_fp, in_file_path.c_str(), "rb");
	fopen_s(&m_out_fp, out_file_path.c_str(), "wb");

	if (NULL != m_in_fp)
	{
		m_in_file_info.m_cur_pos = 0;
		fseek(m_in_fp, 0, SEEK_END);
		fgetpos(m_in_fp, &(m_in_file_info.m_len));
		fseek(m_in_fp, 0, SEEK_SET);
	}

	if (NULL != m_out_fp)
	{
		m_out_file_info.m_cur_pos = 0;
		m_out_file_info.m_len = 0;
		fseek(m_out_fp, 0, SEEK_SET);
	}

	m_memory_data.m_begin = new char[MEMORY_SIZE];
	m_memory_data.m_len = 0;

	m_slice_arr = NULL;
	m_slice_arr = new MEMORY_DATA[THREAD_NUM];
	m_cur_slice = 0;
}

CSGUnpack::~CSGUnpack()
{
	if (NULL != m_in_fp)
	{
		fclose(m_in_fp);
		m_in_fp = NULL;
	}
	if (NULL != m_out_fp)
	{
		fclose(m_out_fp);
		m_out_fp = NULL;
	}
	if (NULL != m_memory_data.m_begin)
	{
		delete m_memory_data.m_begin;
	}

	DeleteCriticalSection(&m_critical_section);
	if (NULL != m_slice_arr)
	{
		delete []m_slice_arr;
	}

	FREE_LOG();
}

int CSGUnpack::unpack()
{
	clock_t begin = clock();
	if ((NULL == m_in_fp) || (NULL == m_out_fp))
	{
		return -1;
	}
	if (0 != cast_rar_file())
	{
		return -1;
	}
	while (0 == prepare_data())
	{
		sync_bitwise_not();
		store_data();
	}
	LOG("total unpack time consume: %lf\n", (double(clock()-begin))/CLOCKS_PER_SEC);
	return 0;
}

int CSGUnpack::cast_rar_file()
{
	clock_t begin = clock();
	unsigned long long offset = 10268114;
	fseek(m_in_fp, 0, SEEK_SET);
	fseek(m_in_fp, (int)offset, SEEK_SET);
	m_in_file_info.m_cur_pos = offset;
	LOG("cast_rar_file time consume: %lf", (double(clock()-begin))/CLOCKS_PER_SEC);
	return 0;
}

int CSGUnpack::prepare_data()
{
	clock_t begin = clock();
	fpos_t cur_pos = m_in_file_info.m_cur_pos;
	fpos_t total_len = m_in_file_info.m_len;
	if (cur_pos >= total_len)
	{
		return -1;
	}

	fpos_t len = (MEMORY_SIZE < total_len-cur_pos) ? (MEMORY_SIZE) : (total_len-cur_pos);
	fread(m_memory_data.m_begin, sizeof(char), (size_t)len, m_in_fp);
	cur_pos = cur_pos+len;
	m_in_file_info.m_cur_pos = cur_pos;
	m_memory_data.m_len = len;
	m_cur_slice = 0;
	LOG("prepare_data time consume: %lf", (double(clock()-begin))/CLOCKS_PER_SEC);
	return 0;
}

int CSGUnpack::store_data()
{
	clock_t begin = clock();
	fwrite(m_memory_data.m_begin, sizeof(char), (size_t)(m_memory_data.m_len), m_out_fp);
	LOG("store_data time consume: %lf", (double(clock()-begin))/CLOCKS_PER_SEC);
	return 0;
}

int CSGUnpack::prepare_param_space(int thread_id)
{
	EnterCriticalSection(&m_critical_section);
	if (m_cur_slice >= m_memory_data.m_len)
	{
		LeaveCriticalSection(&m_critical_section);
		return -1;
	}
	m_slice_arr[thread_id].m_begin = m_memory_data.m_begin + m_cur_slice;
	m_slice_arr[thread_id].m_len = (SLICE_SIZE <= (m_memory_data.m_len-m_cur_slice)) ? SLICE_SIZE:(m_memory_data.m_len-m_cur_slice);
	m_cur_slice = m_cur_slice += m_slice_arr[thread_id].m_len;
	LeaveCriticalSection(&m_critical_section);
	return 0;
}

void CSGUnpack::sync_bitwise_not()
{
	clock_t begin = clock();
	int slice_id = -1;
	unsigned thread_id = 0;
	HANDLE *handle_arr = new HANDLE[THREAD_NUM];
	THREAD_PARAM *thread_param_arr = new THREAD_PARAM[THREAD_NUM];
	for (int i=0; i<THREAD_NUM; i++)
	{
		thread_param_arr[i].m_thread_id = i;
		thread_param_arr[i].m_obj = this;
		handle_arr[i] = (HANDLE)_beginthreadex(NULL, 0, &CSGUnpack::bitwise_not, &(thread_param_arr[i]), 0, &thread_id);
		//ResumeThread(handle_arr[i]);
	}
	WaitForMultipleObjects(THREAD_NUM, handle_arr, TRUE, INFINITE);
	for (int i=0; i<THREAD_NUM; i++)
	{
		CloseHandle(handle_arr[i]);
	}
	delete []handle_arr;
	delete []thread_param_arr;
	LOG("sync_bitwise_not time consume: %lf", (double(clock()-begin))/CLOCKS_PER_SEC);
}

unsigned int __stdcall CSGUnpack::bitwise_not(void *param)
{
	THREAD_PARAM *thread_param = (THREAD_PARAM *)param;
	unsigned int thread_id = thread_param->m_thread_id;
	CSGUnpack *obj = thread_param->m_obj;
	//printf("begin thread, id = %d\n", thread_id);
	while (0 == obj->prepare_param_space(thread_id))
	{
		//printf("running thread, id = %d\n", thread_id);
		char *ptr = obj->m_slice_arr[thread_id].m_begin;
		char *end = obj->m_slice_arr[thread_id].m_begin + obj->m_slice_arr[thread_id].m_len - 1;
		for (; ptr <= end; ++ptr)
		{
			*ptr = ~(*ptr);
		}
	}
	return 0;
}
